/*
 * This file is part of Ext JS 3.4 Copyright (c) 2011-2013 Sencha Inc Contact: http://www.sencha.com/contact GNU General Public License Usage This file may be
 * used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in
 * the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met:
 * http://www.gnu.org/copyleft/gpl.html. If you are unsure which license is appropriate for your use, please contact the sales department at
 * http://www.sencha.com/contact. Build date: 2013-04-03 15:07:25
 */

/**
 * @class Ext.data.Api
 * @extends Object Ext.data.Api is a singleton designed to manage the data API including methods for validating a developer's DataProxy API. Defines variables
 *          for CRUD actions create, read, update and destroy in addition to a mapping of RESTful HTTP methods GET, POST, PUT and DELETE to CRUD actions.
 * @singleton
 */
Ext.data.Api = (function()
{

	// private validActions. validActions is essentially an inverted hash of Ext.data.Api.actions, where value becomes the key.
	// Some methods in this singleton (e.g.: getActions, getVerb) will loop through actions with the code <code>for (var verb in this.actions)</code>
	// For efficiency, some methods will first check this hash for a match. Those methods which do acces validActions will cache their result here.
	// We cannot pre-define this hash since the developer may over-ride the actions at runtime.
	var validActions = {};

	return {
		actions:
		{
			create: 'create',
			read: 'read',
			update: 'update',
			destroy: 'destroy'
		},

		restActions:
		{
			create: 'POST',
			read: 'GET',
			update: 'PUT',
			destroy: 'DELETE'
		},

		/**
		 * Returns true if supplied action-name is a valid API action defined in <code>{@link #actions}</code> constants
		 * 
		 * @param {String} action Action to test for availability.
		 * @return {Boolean}
		 */
		isAction: function( action )
		{
			return (Ext.data.Api.actions[action]) ? true : false;
		},

		/**
		 * Returns the actual CRUD action KEY "create", "read", "update" or "destroy" from the supplied action-name. This method is used internally and shouldn't
		 * generally need to be used directly. The key/value pair of Ext.data.Api.actions will often be identical but this is not necessarily true. A developer can
		 * override this naming convention if desired. However, the framework internally calls methods based upon the KEY so a way of retreiving the the words
		 * "create", "read", "update" and "destroy" is required. This method will cache discovered KEYS into the private validActions hash.
		 * 
		 * @param {String} name The runtime name of the action.
		 * @return {String/null} returns the action-key, or verb of the user-action or null if invalid.
		 * @nodoc
		 */
		getVerb: function( name )
		{
			if( validActions[name] )
			{
				return validActions[name]; // <-- found in cache. return immediately.
			}
			for( var verb in this.actions )
			{
				if( this.actions[verb] === name )
				{
					validActions[name] = verb;
					break;
				}
			}
			return (validActions[name] !== undefined) ? validActions[name] : null;
		},

		/**
		 * Returns true if the supplied API is valid; that is, check that all keys match defined actions otherwise returns an array of mistakes.
		 * 
		 * @return {String[]|true}
		 */
		isValid: function( api )
		{
			var invalid = [];
			var crud = this.actions; // <-- cache a copy of the actions.
			for( var action in api )
			{
				if( !(action in crud) )
				{
					invalid.push(action);
				}
			}
			return (!invalid.length) ? true : invalid;
		},

		/**
		 * Returns true if the supplied verb upon the supplied proxy points to a unique url in that none of the other api-actions point to the same url. The
		 * question is important for deciding whether to insert the "xaction" HTTP parameter within an Ajax request. This method is used internally and shouldn't
		 * generally need to be called directly.
		 * 
		 * @param {Ext.data.DataProxy} proxy
		 * @param {String} verb
		 * @return {Boolean}
		 */
		hasUniqueUrl: function( proxy, verb )
		{
			var url = (proxy.api[verb]) ? proxy.api[verb].url : null;
			var unique = true;
			for( var action in proxy.api )
			{
				if( (unique = (action === verb) ? true : (proxy.api[action].url != url) ? true : false) === false )
				{
					break;
				}
			}
			return unique;
		},

		//准备HttpProxy的api配置项
		prepare: function( proxy )
		{
			
			/*new Ext.data.HttpProxy(
			{
				api:
				{
					create:
					{
						url: 'create.jsp',
						method: 'PUT'
					},
					get:
					{
						url: 'get.jsp',
						method: 'get'
					}
				}
			});*/
			/*actions:
			{
				create: 'create',
				read: 'read',
				update: 'update',
				destroy: 'destroy'
			},
	
			restActions:
			{
				create: 'POST',
				read: 'GET',
				update: 'PUT',
				destroy: 'DELETE'
			},*/
			
			
			if( !proxy.api )
			{
				proxy.api = {}; // 如果代理没有制定api，则制定空的api
			}
			for( var verb in this.actions ) // 遍历actions中的4中动作
			{
				var action = this.actions[verb];
				
				proxy.api[action] = proxy.api[action] || proxy.url || proxy.directFn;
				
				if( typeof(proxy.api[action]) == 'string' )
				{
					// 每个动作指定一个提交方式
					proxy.api[action] =
					{
						url: proxy.api[action],
						method: (proxy.restful === true) ? Ext.data.Api.restActions[action] : undefined
					};
				}
			}
		},

		/**
		 * Prepares a supplied Proxy to be RESTful. Sets the HTTP method for each api-action to be one of GET, POST, PUT, DELETE according to the defined
		 * {@link #restActions}.
		 * 
		 * @param {Ext.data.DataProxy} proxy
		 */
		restify: function( proxy )
		{
			proxy.restful = true;
			for( var verb in this.restActions )
			{
				proxy.api[this.actions[verb]].method || (proxy.api[this.actions[verb]].method = this.restActions[verb]);
			}
			// TODO: perhaps move this interceptor elsewhere? like into DataProxy, perhaps? Placed here
			// to satisfy initial 3.0 final release of REST features.
			proxy.onWrite = proxy.onWrite.createInterceptor(function( action, o, response, rs )
			{
				var reader = o.reader;
				var res = new Ext.data.Response(
				{
					action: action,
					raw: response
				});

				switch( response.status )
				{
				case 200: // standard 200 response, send control back to HttpProxy#onWrite by returning true from this intercepted #onWrite
					return true;
					break;
				case 201: // entity created but no response returned
					if( Ext.isEmpty(res.raw.responseText) )
					{
						res.success = true;
					}
					else
					{
						// if the response contains data, treat it like a 200
						return true;
					}
					break;
				case 204: // no-content. Create a fake response.
					res.success = true;
					res.data = null;
					break;
				default :
					return true;
					break;
				}
				if( res.success === true )
				{
					this.fireEvent("write", this, action, res.data, res, rs, o.request.arg);
				}
				else
				{
					this.fireEvent('exception', this, 'remote', action, o, res, rs);
				}
				o.request.callback.call(o.request.scope, res.data, res, res.success);

				return false; // <-- false to prevent intercepted function from running.
			}, proxy);
		}
	};
})();

/**
 * Ext.data.Response Experimental. Do not use directly.
 */
Ext.data.Response = function( params, response )
{
	Ext.apply(this, params,
	{
		raw: response
	});
};
Ext.data.Response.prototype =
{
	message: null,
	success: false,
	status: null,
	root: null,
	raw: null,

	getMessage: function()
	{
		return this.message;
	},
	getSuccess: function()
	{
		return this.success;
	},
	getStatus: function()
	{
		return this.status;
	},
	getRoot: function()
	{
		return this.root;
	},
	getRawResponse: function()
	{
		return this.raw;
	}
};

/**
 * @class Ext.data.Api.Error
 * @extends Ext.Error Error class for Ext.data.Api errors
 */
Ext.data.Api.Error = Ext.extend(Ext.Error,
{
	constructor: function( message, arg )
	{
		this.arg = arg;
		Ext.Error.call(this, message);
	},
	name: 'Ext.data.Api'
});
Ext.apply(Ext.data.Api.Error.prototype,
{
	lang:
	{
		'action-url-undefined': 'No fallback url defined for this action.  When defining a DataProxy api, please be sure to define an url for each CRUD action in Ext.data.Api.actions or define a default url in addition to your api-configuration.',
		'invalid': 'received an invalid API-configuration.  Please ensure your proxy API-configuration contains only the actions defined in Ext.data.Api.actions',
		'invalid-url': 'Invalid url.  Please review your proxy configuration.',
		'execute': 'Attempted to execute an unknown action.  Valid API actions are defined in Ext.data.Api.actions"'
	}
});
